﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Practices.Prism.Mvvm;
using Microsoft.Practices.Prism.PubSubEvents;
using Hopscotch.Common.Contracts.AppService;
using Hopscotch.Common.Logging;
using Hopscotch.Common.Services.Wcf;
using Hopscotch.Presentation.Configuration;

namespace Hopscotch.Application.Shell.Login
{
    internal interface ILoginViewModel
    {
        void Login(string username, string password);
        void Close(bool isClosing = false);
    }

    internal class LoginViewModel : BindableBase, ILoginViewModel
    {
        private readonly ILogger logger;
        private readonly LoginInfo loginInfo;
        private bool cleanCloseRequested;

        public IList<IEnvironmentConfiguration> Environments { get; private set; }

        /// <summary>
        /// Default constructor used by the designer
        /// </summary>
        public LoginViewModel()
        {
            statusMessage = "Loading...";
            errorMessage = "Error Message...";
            errorDetail = "Stack Trace...";
            //IsFatalError = true;
        }

        public LoginViewModel(
            IEventAggregator eventAggregator,
            IEnvironmentsConfiguration environmentsConfiguration,
            LoginInfo loginInfo)
        {
            this.loginInfo = loginInfo;
            this.logger = AppLogger.CreateLogger<LoginViewModel>();

            Environments = environmentsConfiguration.Environments.ToList();
            Environment = Environments.FirstOrDefault();

            eventAggregator.GetEvent<MessageUpdateEvent>().Subscribe(OnMessageUpdate);
            eventAggregator.GetEvent<SplashCloseEvent>().Subscribe(OnSplashClose);
        }

        void OnMessageUpdate(MessageUpdateEvent messageUpdateEvent)
        {
            if (messageUpdateEvent.IsError)
            {
                IsFatalError = true;
                StatusMessage = null;
                IsLoginVisible = false;
                IsBusy = false;

                ErrorMessage = messageUpdateEvent.Message;
                ErrorDetail = messageUpdateEvent.ErrorDetail;
            }
            else
            {
                StatusMessage = messageUpdateEvent.Message;
            }
        }

        void OnSplashClose(SplashCloseEvent splashCloseEvent)
        {
            cleanCloseRequested = true;
            Close(false);
        }

        private bool isBusy;
        public bool IsBusy
        {
            get { return isBusy; }
            set
            {
                this.SetProperty(ref isBusy, value);
            }
        }

        private bool isClosed;
        public bool IsClosed
        {
            get { return isClosed; }
            set
            {
                this.SetProperty(ref isClosed, value);
            }
        }

        private bool isFatalError;
        public bool IsFatalError
        {
            get { return isFatalError; }
            set
            {
                this.SetProperty(ref isFatalError, value);
            }
        }

        private string statusMessage;
        public string StatusMessage
        {
            get { return statusMessage; }
            set
            {
                this.SetProperty(ref statusMessage, value);
            }
        }

        private string errorMessage;
        public string ErrorMessage
        {
            get { return errorMessage; }
            set
            {
                this.SetProperty(ref errorMessage, value);
            }
        }

        private string errorDetail;
        public string ErrorDetail
        {
            get { return errorDetail; }
            set
            {
                this.SetProperty(ref errorDetail, value);
            }
        }

        private bool isLoginVisible = true;
        public bool IsLoginVisible
        {
            get { return isLoginVisible; }
            set
            {
                this.SetProperty(ref isLoginVisible, value);
            }
        }

        private bool isLoginEnabled = true;
        public bool IsLoginEnabled
        {
            get { return isLoginEnabled; }
            set
            {
                this.SetProperty(ref isLoginEnabled, value);
            }
        }

        public IEnvironmentConfiguration Environment
        {
            get { return loginInfo.Environment; }
            set { loginInfo.Environment = value; }
        }

        public void Login(string username, string password)
        {
            var clientConfig = this.Environment.WcfClientConfigFile;

            var authenticationService = new CustomChannelFactory<IAuthenticationService>(clientConfig).CreateChannel();
            Login(authenticationService, this.Environment, username, password);
        }

        internal async void Login(IAuthenticationService authenticationService, IEnvironmentConfiguration environmentConfiguration, string username, string password)
        {
            IsLoginEnabled = false;
            StatusMessage = "Authenticating...";
            ErrorMessage = null;
            ErrorDetail = null;

            //Todo, hash the password...
            bool authenticated = false;
            try
            {
                authenticated = await Task.Factory.StartNew<bool>(() => authenticationService.Login(username, password, environmentConfiguration.EnvironmentId));
                if (authenticated)
                {
                    //We now have the Environment set and user logged-in.
                    //Signal the Prism bootstrapper to proceeed.
                    IsBusy = true;
                    StatusMessage = null;
                    loginInfo.LoginSuccess = true;
                    loginInfo.Signal.Set();
                }
                else
                {
                    StatusMessage = "Login Failed. Username or password not valid.";
                    IsLoginEnabled = true;
                }
            }
            catch (Exception ex)
            {
                IsLoginEnabled = true;
                StatusMessage = null;
                ErrorDetail = ex.ToString();
            }
        }


        /// <summary>
        /// Close Login/splash screen.
        /// If the bootstrapper has errored close the application
        /// </summary>
        /// <param name="isClosing">True if view is already closing</param>
        public void Close(bool isClosing = false)
        {
            if (!isClosing)
            {
                //Close the window
                IsClosed = true;
            }

            bool forceExit = isFatalError ||
                (loginInfo.LoginSuccess && loginInfo.Signal.IsSet && !cleanCloseRequested);

            //If the shell has started successfully and requested the splash to close,
            //this may run twice with no side effects.
            loginInfo.Signal.Set();

            if (forceExit)
            {
                logger.Warning("Forceful exit, application will now terminate");
                //If the bootstrapper has errored, all we can do is display the error and then exit.
                //Or if the user has cancelled the startup, exit
                System.Environment.Exit(-1);
            }
        }
    }
}
